package com.jie.eclipse.pan.xml;

import java.lang.StringBuilder;
import java.util.Stack;

import com.jie.eclipse.pan.utils.StringHelper;
/**
 * XML 编写器
 * @author yanjie
 */
public class SimpleXMLWriter
{
    private Stack childElements = new Stack();
    private StringBuilder internalBuilder = null;
    private int nCurChildCount = 0;
    private String strCurElementName = "";
    /*
    * 子节点层次
    */
    private int nCurChildLevel = -1;



    public SimpleXMLWriter(StringBuilder sb)
    {
        internalBuilder = sb;
    }

    public void WriteRaw(String strRawText)
    {
        internalBuilder.append(strRawText);
    }

    public void WriteStartElement(String strElement)
    {
        nCurChildLevel ++;
       //写入一个元素，需要将上次的元素封闭
       if(StringHelper.Length(strCurElementName)>0)
       {
           //放入堆栈中
           childElements.push(strCurElementName);
           //封闭当前节点
           appendElementEndTag(true);

       }
       strCurElementName = strElement;
       appendElementStartTag();
    }

    
    
    public void WriteComment(String strComment)
    {
       //写入一个元素，需要将上次的元素封闭
       if(StringHelper.Length(strCurElementName)>0)
       {
           //放入堆栈中
           childElements.push(strCurElementName);
           //封闭当前节点
           appendElementEndTag(true);
           strCurElementName = "";
       }
       nCurChildLevel ++;
       appendIndent();
       WriteRaw("<!-- ");
       WriteRaw(strComment);
       WriteRaw(" -->\r\n");   
       nCurChildLevel--;
    }
    
    /**
     * 写入CDATA分区
     * @param strContent
     */
    public void WriteCDATA(String strContent)
    {
    	nCurChildLevel ++;
       //写入一个元素，需要将上次的元素封闭
       if(StringHelper.Length(strCurElementName)>0)
       {
           //放入堆栈中
           childElements.push(strCurElementName);
           //封闭当前节点
           appendElementEndTag(true);

       }
       
       strCurElementName = "";
       
       appendIndent();
       WriteRaw("<![CDATA[");
       WriteRaw(strContent);
       WriteRaw("]]>\r\n");      
       
       nCurChildLevel --;
    }


    /**
     * 写入属性
     *
     * @param strName String
     * @param strValue String
     */
    public void WriteAttributeString(String strName,String strValue)
    {
        internalBuilder.append(" ");
        internalBuilder.append(formatXMLContent(strName,false));
        internalBuilder.append("=\"");
        internalBuilder.append(formatXMLContent(strValue,true));
        internalBuilder.append("\"");
    }

    /**
     * 写入结束符号
     */
    public void WriteEndElement()
    {
        if(StringHelper.Length(strCurElementName)>0)
       {
           internalBuilder.append("/>\r\n");
           strCurElementName ="";
       }
       else
       {
           if(childElements.size()>0)
           {
               String strTemp =(String) childElements.pop();
               appendIndent();
               internalBuilder.append("</"+strTemp+">\r\n");
           }
       }
       nCurChildLevel --;

    }

    private void appendElementEndTag(boolean bFirstPart)
    {
        if(bFirstPart)
            internalBuilder.append(">\r\n");
        else
        {

        }
    }


    private void appendElementStartTag()
    {
        appendIndent();
        internalBuilder.append("<");
        internalBuilder.append(formatXMLContent(strCurElementName,false));
        internalBuilder.append(" ");
    }

    private void appendIndent()
    {
        for(int i = 0;i<nCurChildLevel;i++)
        {
            internalBuilder.append("    ");
        }
    }


    /**
     * 分别用 &amp;、&lt; 和 &gt; 替换字符 &、< 和 >。
     * 用数字字符实体（&#0; 到 &#0x1F）替换范围 0x-0x1F 中（不包括空白字符 0x9、0x10 和 0x13）的字符值。
     * 如果在属性值的上下文中调用 WriteString，则分别用 &quot; 和 &apos; 替换双引号和单引号。
     * @param strOrigin String
     * @param bProperty boolean
     * @return String
     */
    public static String formatXMLContent(String strOrigin,boolean bProperty)
    {
        strOrigin = strOrigin.replace("&","&amp;");
        strOrigin = strOrigin.replace("<","&lt;");
        strOrigin = strOrigin.replace(">","&gt;");

        if(bProperty)
        {
        	strOrigin = strOrigin.replace("\n","&#xA;");
        	strOrigin = strOrigin.replace("\r","&#xD;");
            strOrigin = strOrigin.replace("\"","&quot;");
            strOrigin = strOrigin.replace("'","&apos;");
        }

        return strOrigin;
    }
}
